#include "my_cleanup.h"

my_cleanup_list_t g_cleanup_list = {
    .lock = MY_SPINLOCK_INITLIZER,
    .head.prev = &g_cleanup_list.head,
    .head.next = &g_cleanup_list.head,
};

void
my_cleanup_list_init(my_cleanup_list_t *list)
{
    my_spinlock_init(&list->lock);
    my_queue_init(&list->head);
}


my_cleanup_item_t *
my_cleanup_list_add(my_cleanup_list_t *list,
    my_cleanup_handler_pt handler, void *data)
{
    my_cleanup_item_t *item;
    item = my_mem_calloc(sizeof(my_cleanup_item_t));
    item->handler = handler;
    item->data = data;

    my_spinlock_lock(&list->lock);
    my_queue_insert_tail(&list->head, &item->queue);
    my_spinlock_unlock(&list->lock);

    return item;
}


void
my_cleanup_list_del(my_cleanup_list_t *list, my_cleanup_item_t *item)
{
    if (!my_queue_inited(&item->queue)) {
        return;
    }

    my_spinlock_lock(&list->lock);
    my_queue_remove(&item->queue);
    my_spinlock_unlock(&list->lock);

    my_mem_free(item);
}


void
my_cleanup_list_exec(my_cleanup_list_t *list)
{
    my_cleanup_item_t   *item = NULL;
    my_bool_t           exit = MY_FALSE;
    my_queue_t          *q;

    while (1) {
        item = NULL;
        my_spinlock_lock(&list->lock);
        if (!my_queue_empty(&list->head)) {
            q = my_queue_last(&list->head);
            item = my_queue_data(q, my_cleanup_item_t, queue);
            my_queue_remove(q);
        } else {
            exit = MY_TRUE;
        }
        my_spinlock_unlock(&list->lock);

        if (item) {
            item->handler(item->data);
            my_mem_free(item);
        }

        if (exit) {
            break;
        }
    }
}

